home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 03 - Advanced Graphics / Example 5 / demo.c < prev    next >
Text File  |  1995-03-06  |  16KB  |  540 lines

  1. //
  2. //    File: demo.c
  3. //
  4. //    This file contains the routines that control the demo.
  5. //
  6. //    2/19/95 -- Created by Mick
  7. //
  8.  
  9. // include files
  10.  
  11. #include "global.h"
  12.  
  13. #include <Palettes.h>
  14. #include <QDOffscreen.h>
  15.  
  16. #include "demo.h"
  17.  
  18. #include "main.h"
  19. #include "sprite.h"
  20. #include "update.h"
  21.  
  22. // defines for this file
  23.  
  24. #define kMaxObjects            10                // the maximum number of objects in the demo
  25. #define kClipRectInset        100            // the amount to inset the clip rect
  26.  
  27. // typedefs for this file
  28.  
  29. typedef struct
  30. {
  31.     tSpriteInfo *fSpriteInfo;                                            // the information on the sprite for this object
  32.     unsigned char fIsVisible;                                    // should we draw this object
  33.     unsigned char fWasVisible;                            // was this object visible last time?
  34.     signed long fXPos;                                                        // the x position of this object ( 16:16 fixed )
  35.     signed long fYPos;                                                        // the y position of this object ( 16:16 fixed )
  36.     signed long fDeltaX;                                                    // the x coord of the velocity of this object ( 16:16 fixed )
  37.     signed long fDeltaY;                                                    // the y coord of the velocity of this object ( 16:16 fixed )
  38.     signed short fIntXPos;                                            // the x position as an integer
  39.     signed short fIntYPos;                                            // the y position as an integer
  40. } tObjectInfo;
  41.  
  42. // global function declarations
  43.  
  44. void startupDemo( void );
  45. void shutdownDemo( void );
  46. void demoKey( unsigned char inKey );
  47. void doDemoFrame( void );
  48.  
  49. // global data owned by this file
  50.  
  51. GWorldPtr gOffscreenBuffer;                                        // the port and gdevice of the offscreen buffer
  52. PixMapHandle gOffscreenPixels;                                // the actual pixmap of the offscreen buffer
  53. Rect gOffscreenRect;                                                                // the size of the offscreen buffer
  54.  
  55. // local function declarations
  56.  
  57. static void moveObjects( void );                                                    // move all the objects
  58. static void blitToScreen( Rect *inCopyRect );            // copy data from offscreen to onscreen
  59. static void restoreBackground( void );                                // redraw the background
  60.  
  61. // static data
  62.  
  63. static tObjectInfo sObjects[ kMaxObjects ];                    // the data for all the objects
  64. static unsigned char sShowUpdateAreaFlag;            // should we show the update area?
  65. static unsigned char sStepMode;                                            // are we running step by step
  66.  
  67. // functions
  68.  
  69. //
  70. //    startupDemo -
  71. //
  72. //    Create and load all the buffers and data needed for the demo.
  73. //
  74.  
  75. void startupDemo( void )
  76. {
  77.     unsigned short indexCounter;                // a counter to scan the object array
  78.  
  79.     // create the offscreen gworld
  80.     NewGWorld( &gOffscreenBuffer, 8, &( gMainWindow->portRect ), gAppColorTable, ( GDHandle )kNil, keepLocal );
  81.  
  82.     // get the pixel map and rect
  83.     gOffscreenPixels = GetGWorldPixMap( gOffscreenBuffer );
  84.     gOffscreenRect = gMainWindow->portRect;
  85.  
  86.     // load the sprite
  87.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  88.         {
  89.             // load the sprites
  90.             sObjects[ indexCounter ].fSpriteInfo = loadSprite( kNumberOne + indexCounter );
  91.             
  92.             // give it a random location and velocity
  93.             sObjects[ indexCounter ].fIntXPos = Random() & 0x00FF;
  94.             sObjects[ indexCounter ].fIntYPos = Random() & 0x00FF;
  95.             sObjects[ indexCounter ].fXPos = ( sObjects[ indexCounter ].fIntXPos ) << 16;
  96.             sObjects[ indexCounter ].fYPos = ( sObjects[ indexCounter ].fIntYPos ) << 16;
  97.             sObjects[ indexCounter ].fDeltaX = Random() << 4;
  98.             sObjects[ indexCounter ].fDeltaY = Random() << 4;
  99.             
  100.             // initially, it is visible (but it was not last frame)
  101.             sObjects[ indexCounter ].fIsVisible = kTrue;
  102.             sObjects[ indexCounter ].fWasVisible = kFalse;
  103.         }
  104.     
  105.     // set the initial flags
  106.     sStepMode = kFalse;
  107.     sShowUpdateAreaFlag = kFalse;
  108. }
  109.  
  110.  
  111. //
  112. //    shutdownDemo -
  113. //
  114. //    Release all the memory used by the demo.
  115. //
  116.  
  117. void shutdownDemo( void )
  118. {
  119.     unsigned short indexCounter;                // a counter to scan the object array
  120.     
  121.     // dump the sprites
  122.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  123.         {
  124.             disposeSprite( sObjects[ indexCounter ].fSpriteInfo );
  125.         }
  126.     
  127.     // dump the offscreen gworld
  128.     DisposeGWorld( gOffscreenBuffer );
  129. }
  130.  
  131.  
  132. //
  133. //    demoKey -
  134. //
  135. //    Handle a key stroke -- allows the user to set options while the demo runs.
  136. //
  137.  
  138. void demoKey( unsigned char inKey )
  139. {
  140.     // depending on the key pressed
  141.     switch( inKey )
  142.         {
  143.             case 'u':        // turn show update are on/off
  144.                 sShowUpdateAreaFlag = !sShowUpdateAreaFlag;
  145.                 break;
  146.             case 's':        // turn step mode on and off
  147.                 sStepMode = !sStepMode;
  148.                 break;
  149.             case ' ':        // step one frame (if we are in step mode)
  150.                 if ( sStepMode )
  151.                     {
  152.                         // allow one frame, by temporarily turning off step mode
  153.                         sStepMode = kFalse;
  154.                         doDemoFrame();
  155.                         sStepMode = kTrue;
  156.                     }
  157.                 break;
  158.                 
  159.             case '1':        // turn this number on and off
  160.                 sObjects[ 0 ].fIsVisible = !sObjects[ 0 ].fIsVisible;
  161.                 break;
  162.             case '2':        // turn this number on and off
  163.                 sObjects[ 1 ].fIsVisible = !sObjects[ 1 ].fIsVisible;
  164.                 break;
  165.             case '3':        // turn this number on and off
  166.                 sObjects[ 2 ].fIsVisible = !sObjects[ 2 ].fIsVisible;
  167.                 break;
  168.             case '4':        // turn this number on and off
  169.                 sObjects[ 3 ].fIsVisible = !sObjects[ 3 ].fIsVisible;
  170.                 break;
  171.             case '5':        // turn this number on and off
  172.                 sObjects[ 4 ].fIsVisible = !sObjects[ 4 ].fIsVisible;
  173.                 break;
  174.             case '6':        // turn this number on and off
  175.                 sObjects[ 5 ].fIsVisible = !sObjects[ 5 ].fIsVisible;
  176.                 break;
  177.             case '7':        // turn this number on and off
  178.                 sObjects[ 6 ].fIsVisible = !sObjects[ 6 ].fIsVisible;
  179.                 break;
  180.             case '8':        // turn this number on and off
  181.                 sObjects[ 7 ].fIsVisible = !sObjects[ 7 ].fIsVisible;
  182.                 break;
  183.             case '9':        // turn this number on and off
  184.                 sObjects[ 8 ].fIsVisible = !sObjects[ 8 ].fIsVisible;
  185.                 break;
  186.             case '0':        // turn this number on and off
  187.                 sObjects[ 9 ].fIsVisible = !sObjects[ 9 ].fIsVisible;
  188.                 break;
  189.                 
  190.             case '!':        // freeze/unfreeze this number
  191.                 if ( sObjects[ 0 ].fDeltaX == 0 && sObjects[ 0 ].fDeltaY == 0 )
  192.                     {
  193.                         sObjects[ 0 ].fDeltaX = Random() << 4;
  194.                         sObjects[ 0 ].fDeltaY = Random() << 4;
  195.                     }
  196.                 else
  197.                     {
  198.                         sObjects[ 0 ].fDeltaX = 0;
  199.                         sObjects[ 0 ].fDeltaY = 0;
  200.                     }
  201.                 break;
  202.             case '@':        // freeze/unfreeze this number
  203.                 if ( sObjects[ 1 ].fDeltaX == 0 && sObjects[ 1 ].fDeltaY == 0 )
  204.                     {
  205.                         sObjects[ 1 ].fDeltaX = Random() << 4;
  206.                         sObjects[ 1 ].fDeltaY = Random() << 4;
  207.                     }
  208.                 else
  209.                     {
  210.                         sObjects[ 1 ].fDeltaX = 0;
  211.                         sObjects[ 1 ].fDeltaY = 0;
  212.                     }
  213.                 break;
  214.             case '#':        // freeze/unfreeze this number
  215.                 if ( sObjects[ 2 ].fDeltaX == 0 && sObjects[ 2 ].fDeltaY == 0 )
  216.                     {
  217.                         sObjects[ 2 ].fDeltaX = Random() << 4;
  218.                         sObjects[ 2 ].fDeltaY = Random() << 4;
  219.                     }
  220.                 else
  221.                     {
  222.                         sObjects[ 2 ].fDeltaX = 0;
  223.                         sObjects[ 2 ].fDeltaY = 0;
  224.                     }
  225.                 break;
  226.             case '$':        // freeze/unfreeze this number
  227.                 if ( sObjects[ 3 ].fDeltaX == 0 && sObjects[ 3 ].fDeltaY == 0 )
  228.                     {
  229.                         sObjects[ 3 ].fDeltaX = Random() << 4;
  230.                         sObjects[ 3 ].fDeltaY = Random() << 4;
  231.                     }
  232.                 else
  233.                     {
  234.                         sObjects[ 3 ].fDeltaX = 0;
  235.                         sObjects[ 3 ].fDeltaY = 0;
  236.                     }
  237.                 break;
  238.             case '%':        // freeze/unfreeze this number
  239.                 if ( sObjects[ 4 ].fDeltaX == 0 && sObjects[ 4 ].fDeltaY == 0 )
  240.                     {
  241.                         sObjects[ 4 ].fDeltaX = Random() << 4;
  242.                         sObjects[ 4 ].fDeltaY = Random() << 4;
  243.                     }
  244.                 else
  245.                     {
  246.                         sObjects[ 4 ].fDeltaX = 0;
  247.                         sObjects[ 4 ].fDeltaY = 0;
  248.                     }
  249.                 break;
  250.             case '^':        // freeze/unfreeze this number
  251.                 if ( sObjects[ 5 ].fDeltaX == 0 && sObjects[ 5 ].fDeltaY == 0 )
  252.                     {
  253.                         sObjects[ 5 ].fDeltaX = Random() << 4;
  254.                         sObjects[ 5 ].fDeltaY = Random() << 4;
  255.                     }
  256.                 else
  257.                     {
  258.                         sObjects[ 5 ].fDeltaX = 0;
  259.                         sObjects[ 5 ].fDeltaY = 0;
  260.                     }
  261.                 break;
  262.             case '&':        // freeze/unfreeze this number
  263.                 if ( sObjects[ 6 ].fDeltaX == 0 && sObjects[ 6 ].fDeltaY == 0 )
  264.                     {
  265.                         sObjects[ 6 ].fDeltaX = Random() << 4;
  266.                         sObjects[ 6 ].fDeltaY = Random() << 4;
  267.                     }
  268.                 else
  269.                     {
  270.                         sObjects[ 6 ].fDeltaX = 0;
  271.                         sObjects[ 6 ].fDeltaY = 0;
  272.                     }
  273.                 break;
  274.             case '*':        // freeze/unfreeze this number
  275.                 if ( sObjects[ 7 ].fDeltaX == 0 && sObjects[ 7 ].fDeltaY == 0 )
  276.                     {
  277.                         sObjects[ 7 ].fDeltaX = Random() << 4;
  278.                         sObjects[ 7 ].fDeltaY = Random() << 4;
  279.                     }
  280.                 else
  281.                     {
  282.                         sObjects[ 7 ].fDeltaX = 0;
  283.                         sObjects[ 7 ].fDeltaY = 0;
  284.                     }
  285.                 break;
  286.             case '(':        // freeze/unfreeze this number
  287.                 if ( sObjects[ 8 ].fDeltaX == 0 && sObjects[ 8 ].fDeltaY == 0 )
  288.                     {
  289.                         sObjects[ 8 ].fDeltaX = Random() << 4;
  290.                         sObjects[ 8 ].fDeltaY = Random() << 4;
  291.                     }
  292.                 else
  293.                     {
  294.                         sObjects[ 8 ].fDeltaX = 0;
  295.                         sObjects[ 8 ].fDeltaY = 0;
  296.                     }
  297.                 break;
  298.             case ')':        // freeze/unfreeze this number
  299.                 if ( sObjects[ 9 ].fDeltaX == 0 && sObjects[ 9 ].fDeltaY == 0 )
  300.                     {
  301.                         sObjects[ 9 ].fDeltaX = Random() << 4;
  302.                         sObjects[ 9 ].fDeltaY = Random() << 4;
  303.                     }
  304.                 else
  305.                     {
  306.                         sObjects[ 9 ].fDeltaX = 0;
  307.                         sObjects[ 9 ].fDeltaY = 0;
  308.                     }
  309.                 break;
  310.                 
  311.             default:        // unknown key
  312.                 SysBeep( 0 );
  313.                 break;
  314.         }
  315. }
  316.  
  317.  
  318. //
  319. //    doDemoFrame -
  320. //
  321. //    Move the demo ahead one frame -- move the sprites and draw.
  322. //
  323.  
  324. void doDemoFrame( void )
  325. {
  326.     CGrafPtr oldPort;                    // the graf port that is in place when we are called
  327.     GDHandle oldDevice;            // the gdevice that is in place when we are called
  328.     signed short indexCounter;            // a counter to scan all the objects
  329.     Rect updateRect;                            // the rect that needs to be updated
  330.     Point drawPoint;                        // where to draw the sprite
  331.         
  332.     // if we are in step mode, return
  333.     if ( sStepMode )
  334.         {
  335.             return;
  336.         }
  337.  
  338.     // save the current port and gdevice
  339.     GetGWorld( &oldPort, &oldDevice );
  340.     
  341.     // set the offscreen buffer as current ( and lock the pixel map )
  342.     SetGWorld( gOffscreenBuffer, ( GDHandle )kNil );
  343.     LockPixels( gOffscreenPixels );
  344.     
  345.     // restore the background
  346.     restoreBackground();
  347.     
  348.     // move the objects
  349.     moveObjects();
  350.     
  351.     // determine the update rect
  352.     if ( getUpdateRect( &updateRect ) )
  353.         {
  354.             // there is a rect to update
  355.             
  356.             // if the user wants it, draw the update rect
  357.             if ( sShowUpdateAreaFlag )
  358.                 {
  359.                     FrameRect( &updateRect );
  360.                 }
  361.             
  362.             // setup to draw the sprites
  363.             startSpriteDraw( &updateRect, gOffscreenPixels );
  364.                         
  365.             // draw the sprites from back to front
  366.             for( indexCounter = kMaxObjects - 1; indexCounter >= 0; indexCounter-- )
  367.                 {
  368.                     if ( sObjects[ indexCounter ].fIsVisible )
  369.                         {
  370.                             drawPoint.h = ( sObjects[ indexCounter ].fIntXPos ) ;
  371.                             drawPoint.v = ( sObjects[ indexCounter ].fIntYPos );
  372.                             drawSprite( sObjects[ indexCounter ].fSpriteInfo, drawPoint );
  373.                         }
  374.                 }
  375.             
  376.             // shutdown the shape draw
  377.             endSpriteDraw();
  378.             
  379.             if ( sShowUpdateAreaFlag )
  380.                 {
  381.                     // copy the buffer to the screen
  382.                     blitToScreen( &gOffscreenRect );
  383.                 }
  384.             else
  385.                 {
  386.                     // copy the buffer to the screen
  387.                     blitToScreen( &updateRect );
  388.                 }
  389.         }
  390.     
  391.     // restore the current port and gdevice
  392.     UnlockPixels( gOffscreenPixels );
  393.     SetGWorld( oldPort, oldDevice );
  394. }
  395.  
  396.  
  397. //
  398. //    moveObjects -
  399. //
  400. //    Move all the objects.
  401. //
  402.  
  403. void moveObjects( void )
  404. {
  405.     unsigned short indexCounter;            // a counter to scan all the objects
  406.     Rect objectRect;                                                            // the rectangle that bounds the object
  407.     
  408.     // clear the update area
  409.     clearUpdate();
  410.     
  411.     // concider each object
  412.     for( indexCounter = 0; indexCounter < kMaxObjects; indexCounter++ )
  413.         {
  414.             // if this object has no velocity, we can ignore it
  415.             if ( sObjects[ indexCounter ].fDeltaX == 0 && sObjects[ indexCounter ].fDeltaY == 0 )
  416.                 {
  417.                     continue;
  418.                 }
  419.             
  420.             // if the object was visible last time
  421.             if ( sObjects[ indexCounter ].fWasVisible )
  422.                 {
  423.                     // determine the object's rect
  424.                     objectRect = ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect;
  425.                     objectRect.left += sObjects[ indexCounter ].fIntXPos;
  426.                     objectRect.top += sObjects[ indexCounter ].fIntYPos;
  427.                     objectRect.right += sObjects[ indexCounter ].fIntXPos;
  428.                     objectRect.bottom += sObjects[ indexCounter ].fIntYPos;
  429.         
  430.                     // add the old rect to the update (so it will be erased)
  431.                     addRectToUpdate( &objectRect );
  432.                 }
  433.  
  434.             // apply the velocity
  435.             sObjects[ indexCounter ].fXPos += sObjects[ indexCounter ].fDeltaX;
  436.             sObjects[ indexCounter ].fYPos += sObjects[ indexCounter ].fDeltaY;
  437.             
  438.             // if the object has hit an edge, bounce it
  439.             if( sObjects[ indexCounter ].fXPos < ( gOffscreenRect.left << 16 ) )
  440.                 {
  441.                     sObjects[ indexCounter ].fDeltaX *= -1;
  442.                     sObjects[ indexCounter ].fXPos += ( gOffscreenRect.left << 16 ) - sObjects[ indexCounter ].fXPos;
  443.                 }
  444.             if( sObjects[ indexCounter ].fYPos < ( gOffscreenRect.top << 16 ) )
  445.                 {
  446.                     sObjects[ indexCounter ].fDeltaY *= -1;
  447.                     sObjects[ indexCounter ].fYPos += ( gOffscreenRect.top << 16 ) - sObjects[ indexCounter ].fYPos;
  448.                 }
  449.             if( sObjects[ indexCounter ].fXPos + 
  450.                     ( ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.right -
  451.                     ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.left ) << 16 ) >
  452.                     ( gOffscreenRect.right << 16 ) )
  453.                 {
  454.                     sObjects[ indexCounter ].fDeltaX *= -1;
  455.                     sObjects[ indexCounter ].fXPos += ( ( gOffscreenRect.right - 
  456.                             ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.right -
  457.                             ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.left ) ) << 16 ) - 
  458.                             sObjects[ indexCounter ].fXPos;
  459.                 }
  460.             if( sObjects[ indexCounter ].fYPos + 
  461.                     ( ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.bottom -
  462.                     ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.top ) << 16 ) >
  463.                     ( gOffscreenRect.bottom << 16 ) )
  464.                 {
  465.                     sObjects[ indexCounter ].fDeltaY *= -1;
  466.                     sObjects[ indexCounter ].fYPos += ( ( gOffscreenRect.bottom - 
  467.                             ( ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.bottom -
  468.                             ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect.top ) ) << 16 ) - 
  469.                             sObjects[ indexCounter ].fYPos;
  470.                 }
  471.             
  472.             // if the object is visible
  473.             if( sObjects[ indexCounter ].fIsVisible )
  474.                 {
  475.                     // evaluate the fixed numbers to new integer positions
  476.                     sObjects[ indexCounter ].fIntXPos = ( sObjects[ indexCounter ].fXPos ) >> 16;
  477.                     sObjects[ indexCounter ].fIntYPos = ( sObjects[ indexCounter ].fYPos ) >> 16;
  478.                     
  479.                     // determine the object's rect
  480.                     objectRect = ( sObjects[ indexCounter ].fSpriteInfo )->fSpriteRect;
  481.                     objectRect.left += sObjects[ indexCounter ].fIntXPos;
  482.                     objectRect.top += sObjects[ indexCounter ].fIntYPos;
  483.                     objectRect.right += sObjects[ indexCounter ].fIntXPos;
  484.                     objectRect.bottom += sObjects[ indexCounter ].fIntYPos;
  485.         
  486.                     // add the new rect to the update (so it will be drawn)
  487.                     addRectToUpdate( &objectRect );
  488.                 }
  489.             
  490.             // record the this frame's visibility
  491.             sObjects[ indexCounter ].fWasVisible = sObjects[ indexCounter ].fIsVisible;
  492.         }
  493. }
  494.  
  495.  
  496. //
  497. //    blitToScreen -
  498. //
  499. //    This routine updates the screen from the offscreen buffer.
  500. //
  501.  
  502. void blitToScreen( Rect *inCopyRect )
  503. {
  504.     CGrafPtr oldPort;                    // the graf port that is in place when we are called
  505.     GDHandle oldDevice;            // the gdevice that is in place when we are called
  506.  
  507.     // save the current port and gdevice
  508.     GetGWorld( &oldPort, &oldDevice );
  509.  
  510.     // set the drawing environment to the screen
  511.     SetGWorld( ( CWindowPtr )gMainWindow, GetMainDevice() );
  512.  
  513.     // make sure that the fore and back colors are correct to prevent colorize mode
  514.     ForeColor( blackColor );
  515.     BackColor( whiteColor );
  516.     
  517.     // Copy the screen's color table seed into the source pixmap.
  518.     // This will minimize CopyBits' setup time.
  519.     ( *( ( *gOffscreenPixels )->pmTable ) )->ctSeed = ( *( ( *( ( *( GetGDevice() ) )->gdPMap ) )->pmTable ) )->ctSeed;
  520.  
  521.     // copy the buffer to the screen
  522.     CopyBits( ( BitMap * )( *gOffscreenPixels ), &( gMainWindow->portBits ), 
  523.             inCopyRect, inCopyRect, srcCopy, ( RgnHandle )kNil );
  524.  
  525.     // restore the current port and gdevice
  526.     SetGWorld( oldPort, oldDevice );
  527. }
  528.  
  529.  
  530. //
  531. //    restoreBackground -
  532. //
  533. //    This routine redraws the background over changed areas.
  534. //
  535.  
  536. void restoreBackground( void )
  537. {
  538.     // erase the entire offscreen
  539.     EraseRect( &gOffscreenRect );
  540. }